{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 加载 MNIST 数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Using TensorFlow backend.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(60000, 28, 28) <class 'numpy.ndarray'>\n",
      "(60000,) <class 'numpy.ndarray'>\n"
     ]
    }
   ],
   "source": [
    "from keras.datasets import mnist\n",
    "\n",
    "(x_train, y_train), (x_test, y_test) = mnist.load_data('mnist/mnist.npz')\n",
    "\n",
    "print(x_train.shape, type(x_train))\n",
    "print(y_train.shape, type(y_train))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 数据处理：规范化\n",
    "\n",
    "`channels_last` corresponds to inputs with shape  (batch, height, width, channels) while `channels_first` corresponds to inputs with shape  (batch, channels, height, width).\n",
    "\n",
    "It defaults to the image_data_format value found in your Keras config file at ~/.keras/keras.json. If you never set it, then it will be `channels_last`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(60000, 28, 28, 1) <class 'numpy.ndarray'>\n",
      "(10000, 28, 28, 1) <class 'numpy.ndarray'>\n"
     ]
    }
   ],
   "source": [
    "from keras import backend as K\n",
    "\n",
    "img_rows, img_cols = 28, 28\n",
    "\n",
    "if K.image_data_format() == 'channels_first':\n",
    "    x_train = x_train.reshape(x_train.shape[0], 1, img_rows, img_cols)\n",
    "    x_test = x_test.reshape(x_test.shape[0], 1, img_rows, img_cols)\n",
    "    input_shape = (1, img_rows, img_cols)\n",
    "else:\n",
    "    x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1)\n",
    "    x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 1)\n",
    "    input_shape = (img_rows, img_cols, 1)\n",
    "\n",
    "\n",
    "print(x_train.shape, type(x_train))\n",
    "print(x_test.shape, type(x_test))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "60000 train samples\n",
      "10000 test samples\n"
     ]
    }
   ],
   "source": [
    "# 将数据类型转换为float32\n",
    "X_train = x_train.astype('float32')\n",
    "X_test = x_test.astype('float32')\n",
    "# 数据归一化\n",
    "X_train /= 255\n",
    "X_test /= 255\n",
    "\n",
    "print(X_train.shape[0], 'train samples')\n",
    "print(X_test.shape[0], 'test samples')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 统计训练数据中各标签数量"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0 1 2 3 4 5 6 7 8 9] [5923 6742 5958 6131 5842 5421 5918 6265 5851 5949]\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "label, count = np.unique(y_train, return_counts=True)\n",
    "print(label, count)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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s379+uhg+MaNG/n4449JTU0t6G6XaGFe7zt37uSKK66I3m0y1vjx42nevDktWrSgS5cu0XEHyV2Y9xvAAw88EL08GWDVqlX85S9/YdGiRSxfvpxp06ad8IytsChw5CK/B7RatWr84x//YOXKlYwZM4Zbb701WsbEiRNZvnw5q1atYseOHbz++utJ2ZeT8emnn0avaOrYsSNvvPHGCfO7O+7OgQMHcHf27t1L7dq181VXVn93ixYtSE9P59FHH2XKlCmkpKTw/vvv0717dzp37gzAggULaNmyJWlpafTq1YuXXnopelbTu3dvLr74Yj755BNSUlIYOXLkSfwHSob8vt7LlSvHkCFDePbZZ4/ZvqBfGORYJ3q/vfnmm9SvX5+mTZtG09asWUPbtm2jF4t8//vfZ/LkyXFvpwJHPuV2QC+44ILoB2PTpk35+uuvOXToEED0FDMzM5PDhw8fc+VPUWRmdOrUidatWzNixAggsk9Tp04F4PXXX2fz5s3R/OvXr+eCCy7g+9//fvT+52XKlGH48OE0b96c2rVr89FHH0WvlMpLVn/3ihUrePPNN6lSpQq9evUiIyODQ4cOsW3bNt5++20Abr31VlavXk16ejrLli2jZ8+e0XLGjx/P1q1bOXLkCBkZGfmqP6dv3enp6bRr1y6alv0Sz8WLF1O6dGkmTZoUzZ81j1KLFi147bXX8rXfRVFur/cKFSpw2WWXHTeWdDJfGJIpzHGfO3cuZ511VvT3Q4899li0nIL8dijM+23//v08/fTTDBo06JgymjVrxvz589m5cycHDx5k+vTpx7xH40VjHDnIOqBmxt13303//v2jB7Rnz57HfYBmeeONN2jVqhWnn356NK1z584sWrSIrl27ct111yVyN0JbsGABderUYfv27XTs2JFGjRoxatQofv7znzNkyBB+9KMfUbZsWQBq1arFpk2bqFq1KkuXLqVnz56sXr2aM844g+HDh/Phhx9y7rnn8rOf/YynnnqKX//613nWn+z+7jlz5hwzFvPwww8zaNAgunbtyvTp03n44YeZO3cuAEePHuWRRx6hU6dO0fzly5fn1VdfpUGDBnz++ee0bt2azp07U7ly5ULdn8JW0Nd7rNgvDBUqVKBBgwa8+OKLCdqDkxPmuF9++eVMmzbtuDJuv/12BgwYwG233ZbvesO83wYPHsz9999P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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig = plt.figure()\n",
    "plt.bar(label, count, width = 0.7, align='center')\n",
    "plt.title(\"Label Distribution\")\n",
    "plt.xlabel(\"Label\")\n",
    "plt.ylabel(\"Count\")\n",
    "plt.xticks(label)\n",
    "plt.ylim(0,7500)\n",
    "\n",
    "for a,b in zip(label, count):\n",
    "    plt.text(a, b, '%d' % b, ha='center', va='bottom',fontsize=10)\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 数据处理：one-hot 编码\n",
    "\n",
    "### 几种编码方式的对比\n",
    "\n",
    "| Binary | Gray code | One-hot  |\n",
    "| ------ | --------- | -------- |\n",
    "| 000    | 000       | 00000001 |\n",
    "| 001    | 001       | 00000010 |\n",
    "| 010    | 011       | 00000100 |\n",
    "| 011    | 010       | 00001000 |\n",
    "| 100    | 110       | 00010000 |\n",
    "| 101    | 111       | 00100000 |\n",
    "| 110    | 101       | 01000000 |\n",
    "| 111    | 100       | 10000000 |\n",
    "\n",
    "### one-hot 应用\n",
    "![](https://shanelynnwebsite-mid9n9g1q9y8tt.netdna-ssl.com/wp-content/uploads/2018/01/one-hot-word-embedding-vectors.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Shape before one-hot encoding:  (60000,)\n",
      "Shape after one-hot encoding:  (60000, 10)\n"
     ]
    }
   ],
   "source": [
    "from keras.utils import np_utils\n",
    "\n",
    "n_classes = 10\n",
    "print(\"Shape before one-hot encoding: \", y_train.shape)\n",
    "Y_train = np_utils.to_categorical(y_train, n_classes)\n",
    "print(\"Shape after one-hot encoding: \", Y_train.shape)\n",
    "Y_test = np_utils.to_categorical(y_test, n_classes)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "5\n",
      "[0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]\n"
     ]
    }
   ],
   "source": [
    "print(y_train[0])\n",
    "print(Y_train[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 使用 Keras sequential model 定义 MNIST CNN 网络\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "from keras.models import Sequential\n",
    "from keras.layers import Dense, Dropout, Flatten\n",
    "from keras.layers import Conv2D, MaxPooling2D\n",
    "\n",
    "model = Sequential()\n",
    "## Feature Extraction\n",
    "# 第1层卷积，32个3x3的卷积核 ，激活函数使用 relu\n",
    "model.add(Conv2D(filters=32, kernel_size=(3, 3), activation='relu',\n",
    "                 input_shape=input_shape))\n",
    "\n",
    "# 第2层卷积，64个3x3的卷积核，激活函数使用 relu\n",
    "model.add(Conv2D(filters=64, kernel_size=(3, 3), activation='relu'))\n",
    "\n",
    "# 最大池化层，池化窗口 2x2\n",
    "model.add(MaxPooling2D(pool_size=(2, 2)))\n",
    "\n",
    "# Dropout 25% 的输入神经元\n",
    "model.add(Dropout(0.25))\n",
    "\n",
    "# 将 Pooled feature map 摊平后输入全连接网络\n",
    "model.add(Flatten())\n",
    "\n",
    "## Classification\n",
    "# 全联接层\n",
    "model.add(Dense(128, activation='relu'))\n",
    "\n",
    "# Dropout 50% 的输入神经元\n",
    "model.add(Dropout(0.5))\n",
    "\n",
    "# 使用 softmax 激活函数做多分类，输出各数字的概率\n",
    "model.add(Dense(n_classes, activation='softmax'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 查看 MNIST CNN 模型网络结构"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "conv2d_1 (Conv2D)            (None, 26, 26, 32)        320       \n",
      "_________________________________________________________________\n",
      "conv2d_2 (Conv2D)            (None, 24, 24, 64)        18496     \n",
      "_________________________________________________________________\n",
      "max_pooling2d_1 (MaxPooling2 (None, 12, 12, 64)        0         \n",
      "_________________________________________________________________\n",
      "dropout_1 (Dropout)          (None, 12, 12, 64)        0         \n",
      "_________________________________________________________________\n",
      "flatten_1 (Flatten)          (None, 9216)              0         \n",
      "_________________________________________________________________\n",
      "dense_1 (Dense)              (None, 128)               1179776   \n",
      "_________________________________________________________________\n",
      "dropout_2 (Dropout)          (None, 128)               0         \n",
      "_________________________________________________________________\n",
      "dense_2 (Dense)              (None, 10)                1290      \n",
      "=================================================================\n",
      "Total params: 1,199,882\n",
      "Trainable params: 1,199,882\n",
      "Non-trainable params: 0\n",
      "_________________________________________________________________\n"
     ]
    }
   ],
   "source": [
    "model.summary()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[None, 26, 26, 32]\n",
      "[None, 24, 24, 64]\n",
      "[None, 12, 12, 64]\n",
      "[None, 12, 12, 64]\n",
      "[None, None]\n",
      "[None, 128]\n",
      "[None, 128]\n",
      "[None, 10]\n"
     ]
    }
   ],
   "source": [
    "for layer in model.layers:\n",
    "    print(layer.get_output_at(0).get_shape().as_list())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 编译模型\n",
    "\n",
    "[model.compile()](https://keras.io/models/sequential/#compile)\n",
    "\n",
    "```python\n",
    "compile(optimizer, loss=None, metrics=None, loss_weights=None, sample_weight_mode=None, weighted_metrics=None, target_tensors=None)\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.compile(loss='categorical_crossentropy', metrics=['accuracy'], optimizer='adam')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 训练模型，并将指标保存到 history 中\n",
    "\n",
    "[model.fit()](https://keras.io/models/sequential/#fit)\n",
    "\n",
    "```python\n",
    "fit(x=None, y=None, batch_size=None, epochs=1, verbose=1, callbacks=None, validation_split=0.0, validation_data=None, shuffle=True, class_weight=None, sample_weight=None, initial_epoch=0, steps_per_epoch=None, validation_steps=None)\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train on 60000 samples, validate on 10000 samples\n",
      "Epoch 1/5\n",
      " - 218s - loss: 0.2313 - acc: 0.9305 - val_loss: 0.0511 - val_acc: 0.9822\n",
      "Epoch 2/5\n",
      " - 187s - loss: 0.0814 - acc: 0.9758 - val_loss: 0.0437 - val_acc: 0.9855\n",
      "Epoch 3/5\n",
      " - 181s - loss: 0.0599 - acc: 0.9816 - val_loss: 0.0335 - val_acc: 0.9895\n",
      "Epoch 4/5\n",
      " - 180s - loss: 0.0514 - acc: 0.9839 - val_loss: 0.0311 - val_acc: 0.9894\n",
      "Epoch 5/5\n",
      " - 179s - loss: 0.0447 - acc: 0.9862 - val_loss: 0.0309 - val_acc: 0.9899\n"
     ]
    }
   ],
   "source": [
    "history = model.fit(X_train,\n",
    "                    Y_train,\n",
    "                    batch_size=128,\n",
    "                    epochs=5,\n",
    "                    verbose=2,\n",
    "                    validation_data=(X_test, Y_test))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 可视化指标"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig = plt.figure()\n",
    "plt.subplot(2,1,1)\n",
    "plt.plot(history.history['acc'])\n",
    "plt.plot(history.history['val_acc'])\n",
    "plt.title('Model Accuracy')\n",
    "plt.ylabel('accuracy')\n",
    "plt.xlabel('epoch')\n",
    "plt.legend(['train', 'test'], loc='lower right')\n",
    "\n",
    "plt.subplot(2,1,2)\n",
    "plt.plot(history.history['loss'])\n",
    "plt.plot(history.history['val_loss'])\n",
    "plt.title('Model Loss')\n",
    "plt.ylabel('loss')\n",
    "plt.xlabel('epoch')\n",
    "plt.legend(['train', 'test'], loc='upper right')\n",
    "plt.tight_layout()\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 保存模型\n",
    "\n",
    "[model.save()](https://keras.io/getting-started/faq/#how-can-i-save-a-keras-model)\n",
    "\n",
    "You can use `model.save(filepath)` to save a Keras model into a single **HDF5 file** which will contain:\n",
    "\n",
    "- the architecture of the model, allowing to re-create the model\n",
    "- the weights of the model\n",
    "- the training configuration (loss, optimizer)\n",
    "- the state of the optimizer, allowing to resume training exactly where you left off.\n",
    "\n",
    "You can then use `keras.models.load_model(filepath)` to reinstantiate your model. load_model will also take care of compiling the model using the saved training configuration (unless the model was never compiled in the first place)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Saved trained model at ./mnist/model/keras_mnist.h5 \n"
     ]
    }
   ],
   "source": [
    "import os\n",
    "import tensorflow.gfile as gfile\n",
    "\n",
    "save_dir = \"./mnist/model/\"\n",
    "\n",
    "if gfile.Exists(save_dir):\n",
    "    gfile.DeleteRecursively(save_dir)\n",
    "gfile.MakeDirs(save_dir)\n",
    "\n",
    "model_name = 'keras_mnist.h5'\n",
    "model_path = os.path.join(save_dir, model_name)\n",
    "model.save(model_path)\n",
    "print('Saved trained model at %s ' % model_path)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 加载模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "from keras.models import load_model\n",
    "\n",
    "mnist_model = load_model(model_path)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 统计模型在测试集上的分类结果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Test Loss: 0.030900797331982175\n",
      "Test Accuracy: 98.99%\n",
      "Classified correctly count: 9899\n",
      "Classified incorrectly count: 101\n"
     ]
    }
   ],
   "source": [
    "loss_and_metrics = mnist_model.evaluate(X_test, Y_test, verbose=2)\n",
    "    \n",
    "print(\"Test Loss: {}\".format(loss_and_metrics[0]))\n",
    "print(\"Test Accuracy: {}%\".format(loss_and_metrics[1]*100))\n",
    "\n",
    "predicted_classes = mnist_model.predict_classes(X_test)\n",
    "\n",
    "correct_indices = np.nonzero(predicted_classes == y_test)[0]\n",
    "incorrect_indices = np.nonzero(predicted_classes != y_test)[0]\n",
    "print(\"Classified correctly count: {}\".format(len(correct_indices)))\n",
    "print(\"Classified incorrectly count: {}\".format(len(incorrect_indices)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "py3",
   "language": "python",
   "name": "py3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
